查看原文
其他

玩转Gradle ,动不动就Build Running,这玩意到底在干嘛?

CoderPig 鸿洋 2021-10-13

本文作者


作者:CoderPig

链接:

https://juejin.cn/post/6950643579643494431

本文由作者授权发布。


Android开发仔基本上都间接或直接接触过Gradle,毕竟用AS导项目都这个坎:卡在Gradle:Build Running



半个钟过去了,还在Build Running,这玩意到底在干嘛???


而学完Gradle,可能有助于你理解这个编译过程,而进行一些编译提速的优化~


当然,好处还不止这个,比如:通过Gradle了解到具体的APP打包过程,可以通过自定义Task或编写Gradle插件的形式对APK体积进行缩减,比如资源混淆工具AndResGuard,还有多渠道打包,编译提速等,反正就是学了不亏。


适逢赶上掘金小册免费学的活动就系统学习下Gradle吧,希望对想学Gradle的朋友有所裨益。

1初识Gradle


1. 自动化构建工具


Gradle的定义如下:


使用纯Java编写,基于Ant、Maven概念开源的自动化构建工具,专注于灵活性和性能,构建脚本摒弃了基于XML的繁琐配置,采用Groovy或Kotlin的特定领域语言(DSL)来编写。

拆词开始:


构建→ 将源码生成可执行程序的过程;


自动化→ 用机器代替手工的一些工作;


构建在Android中的体现就是:编译源码 → 生成.apk可执行文件,在Android官网

https://developer.android.com/studio/build?hl=zh-cn


中有这样一个构建流程图:



简析下流程:


IDE将源代码转成dex,其他内容转换成编译后的资源;


APK打包器将dex和编译后的资源整合成单个apk;


打包器使用zipalign工具对应用进行优化,为apk签名;


当然,这是高度抽象后的流程,实际的打包过程可要复杂得多:


  • aapt命令 → 生成R.java文件;


  • aidl命令 → 生成aidl对应的java文件;


  • javac命令 → 编译java源文件生成class文件;


  • dx.bat → class转换成class.dex文件;


  • ...等


想想如果每次打包apk,都要手工用各种命令、工具按顺序来打包,效率得有多低,碰上要打十几个渠道包的,打包仔直接哭晕。


Tips:不同应用市场可能有不同的统计需求,需要为每个应用市场发布一个安装包,这就是渠道包。

人是容易犯错的,特别是这种手动介入的重复任务,把渠道标识弄乱打错包这类事情时有发生。


可以把这种流水线式重复构建的活写到一个脚本中(构建脚本),每次打包,执行下这个脚本自动化打包即可,使得让开发仔可以心无旁骛地编写功能代码,以此提高开发效率。


构建脚本的内容就是按照构建流程,依次执行命令、调用工具,最后将生成的可执行文件输出到特定目录。


说到脚本,有些童鞋立马上头,准备Python、Bash一把梭,实际上大可不必。


开源的自动化构建工具就很香,没必要重复造轮子,还得自己踩波坑~


2. Ant、Maven、Gradle的区别


Android 早期使用Eclipse作为IDE时,用的自动化构建工具 →Apache Ant,Java编写、平台无关、基于任务链思想,采用XML作为构建脚本,文件默认是build.xml,基础配置模板如下:


<?xml version="1.0" encoding="UTF-8" ?>  
<project name="HelloWorld" default="run" basedir=".">  
<property name="src" value="src"/>  
<property name="dest" value="classes"/>  
<property name="jarfile" value="hello.jar"/>  
<target name="init">  
   <mkdir dir="${dest}"/>  
</target>  
<target name="compile" depends="init">  
   <javac srcdir="${src}" destdir="${dest}"/>  
</target>  
<target name="build" depends="compile">  
   <jar jarfile="${jarfile}" basedir="${dest}"/>  
</target>  
<target name="test" depends="build">  
   <java classname="test.ant.HelloWorld" classpath="${hello_jar}"/>  
</target>  
<target name="clean">  
   <delete dir="${dest}" />  
   <delete file="${hello_jar}" />  
</target>  
</project> 

可以清晰地看到脚本中定义了五个Target,分别是:init、compile、build、test、clean,Target。


它们之间还通过depends定义依赖关系,以此形成了执行的先后顺序,执行build Target前会执行compile,而执行compile前又要init。


Ant 还支持自定义Target,但存在一个问题:没办法管理依赖,项目依赖到第三方库,需要自己手动将正确版本的包拷贝到lib目录下,早期到处找各种jar包的场景依旧历历在目。


所以,带依赖库管理的Apache Maven来了,只需在工程管理文件中(pom.xml)标明需要的包及版本,构建时Maven会自动打包到工程中,示例如下:


<?xml version="1.0" encoding="utf-8"?>
<project ...xmlns...>
    <groupId>cn.coderpig</groupId>
    <artifactId>Test</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.1</version>
        </dependency>
    </dependencies>
</project>

定义了自己的包:cn.coderpig:Test:1.0.0-SNAPSHOT,工程依赖了:com.squareup.okhttp3:okhttp:4.9.1,Maven会自动把okhttp包打进来,没有的话还会自动到网上下载(远程仓库,可通过repository标签指定)。


Maven抛弃了Ant中通过Target定义任务的做法,采用约定优于配置的思想,抽象出一套满足大部分项目的构建生命周期:clean → default → site,要求用户在给定的生命周期中使用插件的方式去完成构建工作。


规范化好是好,但也带来一个问题:自定义构建需求实现麻烦,没办法像Ant那样灵活。


还有一点,Ant和Maven都使用XML定义构建脚本,可读性及扩展性较差,在任务执行上较弱(不支持循环,条件判断麻烦等)。


所以,集Ant和Maven所长,功能更强大的Gradle来了:

① 抛弃繁琐的XML,采用Grovvy DSL或Kotlin DSL来编写构建脚本;


② 可以像Maven一样自动下jar包,即依赖管理;


③ 有默认标准的构建生命周期,也支持灵活的自定义任务等;


④ 构建级别支持从Ant、Maven的逐步迁移;


Tips:DSL(Domain Specific Language),特定领域语言,专注于特定领域问题的计算机语言,如SQL仅支持数据库相关操作,正则表达式只支持字符串的检索替换。以简洁的形式进行表达,直观易懂、使得读、调代码的成本得以降低,就是求精不求广。

简单小结下三者区别:


Ant支持自动化打包逻辑,Maven比它多了自动下jar包,规范了打包逻辑,反而不好定制。Gradle既能自动下jar包,又能自己写脚本,而且脚本写起来比Ant爽。

 3. Gradle的下载配置


Gradle基于JVM,需要Java环境(1.8及以上版本),在Gradle官网

https://services.gradle.org/distributions/


下载对应版本Gradle,此处以6.1.1为例,有四种类型的包可供下载:



下载bin和all都可以,我习惯下后者,有时需要看下源码和文档,下载zip包后解压,配置下环境变量GRADLE_HOME,Windows环境配置示例如下:



Path环境变量新增:



配置完后打开终端,键入:gradle -v验证是否生效。



 4. Gradle Wrapper


Android Studio默认使用Gradle Wrapper而不是直接使用Gradle,命令也是使用gradlew而不是gradle。


这个Gradle Wrapper就是对Gradle的一层封装,使得开发者无需关心项目Gradle的版本变化。


新建一个目录,键入下述命令:


gradle wrapper

生成文件目录结构如下:


├── .gradle
├── gradle
│   └── wrapperwrapper
|       └── gradle-wrapper.jar          // 用于下载所需Gradle;
|       └── gradle-wrapper.properties   // 配置文件;
├── gradlew         //Linux下的可执行脚本;
└── gradlew.bat     //Windows下的可执行脚本;

接着键入:gradlew build编译,检查到配置文件中对应版本的Gradle本地没有时,会启动wrapper进程下载配置Gradle,完事后此进程会自动关闭。



gradle-wrapper.properties配置文件内容如下:



Windows下指向:C:\Users\用户名.gradle目录,打开可以看到下载各个版本的gradle:



封装一层还有个好处:在没有安装Gradle的机器上也可以使用Gradle构建项目,但有一点要注意:


每个Gradle版本对应一个Daemon进程,基本512M起步,电脑配置不佳的情况下,应尽量避免多个版本的Gradle同时运行。建议:自己管理Gradle,即使用本地创建Gradle环境,AS的配置方法如下:



5. 构建脚本初体验


键入下述命令新建一个build.gradle文件然后编译:


touch build.gradle
echo println("Hello Gradle!"); >> build.gradle
gradlew

输出内容如下:



执行gradle时会从当前目录查找名为build.gradlebuild.gradle.kts的文件并执行其中的内容。


除手动创建的方式外,还可以通过gradle init自动初始化不同类型的Project:



此处以Kotlin application工程为例,打开目录查看创建的文件:



可以看到build.gradle中添加了一些依赖,键入gradlew build编译下项目:




6. 包都下到哪里去了


问题来了:下载的第三方依赖库都放哪里去了?


答:~/.gradle/cache/dodules-2/files-2.1/包名/库名/版本号/hash字符串/,示例如下:



如果你不想将gradle相关的下到~/.gradle下,可自行添加环境变量GRADLE_USER_HOME,如:



后续,gradle下载的东西就会放到这个目录下了:



上述改动,在Android Studio不一定会生效哦,有时还需自行配置:



2Gradle的执行架构


当我想删除上面这个C:\Test的目录时,却发现删除不了:



就是有进程在占用这个文件夹,那是什么进程呢?答:daemon进程,可以键入下述命令gradle --status查看一波:



进程id为10276 → 进程处于空闲状态(BUSY表示正在构建任务) → 附加信息:6.1.1,打开任务管理器可以定位到此进程:



我们都知道java代码编译成class字节码后运行在JVM上,那就用jdk自带的jvisualvm.exe查看一波具体信息:



行吧,就是daemon守护进程,进程名为GradleDaemon,所以为啥要让一个守护进程常驻后台呢?


这得先提一提Maven了:


Maven在构建时,会启动一个Maven的JVM进程,构建结束后会关闭此进程,每使用一次Maven构建都要启动一次,其中load所需的jar文件是一个相当耗时的过程。

而Gradle 3.0之后,默认使用Daemon模式:


启动一个非常轻量的client JVM进程,只用于和后台的deamon JVM进程通信。构建完client进程关闭,而deamon进程仍然保留(处于IDLE空闲状态),下次需要构建时,直接启用deamon进程,减少构建的耗时等待。deamon进程默认后台保留三个小时,在此时间段没有被启动则关闭。


3Gradle配置


Gradle配置的地方有三处,参数优先级依次如下:


命令行参数>~/.gradle/gradle.properties>项目根目录/gradle.properties


罗列下较常用的命令行选项,大概过一下有个印象即可,用到再查(更多详细内容可参见:官方文档

https://docs.gradle.org/current/userguide/command_line_interface.html#command_line_interface


# 命令结构
gradle [taskName...] [--option-name...]

# 增量编译:同一个项目中, 同一个 task除非有必要, 否则不会被无意义的执行多次;
# 缓存:无论是否在同一个项目,只要Task输入没变就复用缓存结果,不必真的执行task;

# Tasks执行
gradle myTask   # 执行某个Task
gradle :my-subproject:taskName  # 执行子项目中的Task
gradle my-subproject:taskName   # 同上,不指定子项目,会执行所有子项目的此Task,如gradle clean;
gradle task1 task2  # 运行多个Task
gradle dist --exclude-task test # 将某个task排除在执行外
gradle dist -x test # 同上
gradle test --rerun-tasks   # 强制执行UP-TO-DATE的Task,即不走增量编译,执行全量编译;
gradle test --continue  # 默认情况下,一旦Task失败就会构建失败,通过此参数可继续执行;

# 常见任务(和插件间的Task约定)
gradle build
gradle run
gradle check
gradle clean    # 删除构建目录

# 构建细节
gradle projects # 列出所有子项目
gradle tasks    # 列出所有Task(分配给任务组的Task)
gradle tasks --group="build setup"  # 列出特定任务组的Task
gradle tasks --all  # 列出所有Task
gradle -q help --task libs  # 查看某个Task的详细信息
gradle myTask --scan    # 生成可视化的编译报告
gradle dependencies # 列出项目依赖
gradle -q project:properties    # 列出项目属性列表

# 调试选项
-?,-h,--help  # 帮助信息
-v,--version   # 版本信息
-s, --stacktrace    # 打印出异常堆栈跟踪信息;
-S, --full-stacktrace   # 比上面更完整的信息;

# 性能相关
--build-cache   # 复用缓存
--no-build-cache    # 不复用缓存,默认
--max-workers   # 最大处理器数量
--parallel  # 并行生成项目
--no-parallel   # 不并行生成项目
--priority  # Gradle启动的进程优先级
--profile   # 生成性能报告


# 守护进程
--daemon # 使用deamon进程构建
--no-daemon # 不使用deamon进程构建
--foreground    # 前台进程启动deamon进程
--status    # 查看运行中和最近停止的deamon进程;
--stop  # 停止所有同一版本的deamon进程;

# 日志选项
-q, --quiet # 只记录错误
-w, --warn
-i, --info 
-d, --debug
--console=(auto,plain,rich,verbose) # 指定输出类型
--warning-mode=(all,fail,none,summary)  # 指定警告级别

# 执行选项
--include-build # 复合构建
--offline   # 离线构建
--refresh-dependencies  # 强制清除依赖缓存
--dry-run # 在不实际执行Task的情况下看Task执行顺序
--no-rebuild # 不重复构建项目依赖

# 环境选项
-b, --build-file # 指定构建文件
-c, --settings-file # 指定设置文件
-g, --gradle-user-home  # 指定默认.Gradle目录
-p, --project-dir   # 指定Gradle的开始目录
--project-cache-dir # 指定缓存目录,默认.gradle
-D, --system-prop   # 设置JVM系统属性
-I, --init-script   # 指定初始化脚本
-P, --project-prop  # 指定根项目的项目属性;


Tips:有些属性支持在gradle.properties文件中进行配置,就不用每次命令行输入了,更多可参见:System properties

https://docs.gradle.org/current/userguide/build_environment.html#build_environment


4Gradle基础


1. Gradle构建生命周期


Gradle构建分为三大阶段:



2. 生命周期监听(HOOK)




3. Grovvy基础语法速成


Grovvy是JVM上的脚本语言,基于Java扩展的动态语言,除了兼容Java外,还加入了闭包等新功能。


Gradle会把.gradleGroovy脚本编译成.class java字节码文件在JVM上运行。


Gradle是自动化构建工具,运行在JVM上的一个程序,Groovy是基于JVM的一种语言,他两间的关系就想Android和Java一样。


Gradle中涉及Groovy的语法只是都是比较简单的,学完对Groovy感兴趣可自行移步到官网学习groovy API

http://www.groovy-lang.org/api.html


Android项目采用Gradle构建,默认使用Groovy DSL脚本构建,从Gradle 4.0开始,正式支持Kotlin DSL脚本构建,两者可以共存,本节基于Groovy DSL进行讲解。(个人感觉他两语法实在是太像了,会Kotlin迁移到Groovy无压力~)


①基础规则


注释与Java一致,支持:// 或 /**/


不以分号结尾;


单引号字符串不会对$符号转义,双引号字符串可以使用字符串模板,三引号是带格式的字符串;


方法括号可省略,可不写return,默认返回最后一行代码;


代码块可以作为参数传递。


②定义(使用def关键字定义)


// 定义变量:Groovy支持动态类型,定义时可不指定类型,会自行推导

def a = 1   // 定义整型,Groovy编译器会将所有基本类型都包装成对象类型
def b = "字符串:${a}"  // 定义字符串模板
de double c = 1.0   // 定义Double类型

③声明变量


// 局部变量,仅在声明它们的范围内可见
def dest = "dest"
task copy(type: Copy) {
    from "source"
    into dest
}

// ext额外属性,Gradle域模型中所有增强对象都可以容纳额外的用户定义属性
ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}

task printProperties {
    doLast {
        println springVersion
        println emailNotification
    }
}

// 用类型修饰符声明的变量在闭包中可见,但在方法中不可见
String localScope1 = 'localScope1'

println localScope1

closure = {
    println localScope1
}

def method() {
    try {
        localScope1
    } catch (MissingPropertyException e) {
        println 'localScope1NotAvailable'
    }
}

closure.call()
method()

// 输出结果:
// localScope1
// localScope1
// localScope1NotAvailable

④函数


// 无返回值的函数需使用def关键字,最后一行代码的执行结果就是返回值
// 无参函数
def fun1() { }

// 有参函数
def fun2(def1, def2) { }

// 指定了函数返回类型,则可不加def关键字
String fun3() return "返回值" }

// 简化下,效果同fun3
String fun4() "返回值" }

// 函数调用可以不加括号
println fun3

⑤循环


// i前面b,输出5个测试
for(i = 0;i < 5;i++) {
    println("测试")
}

// 输出6个测试
for(i in 0..5) {
    println("测试")
}

// 如果想输出5个可改成:
for(i in 0..<5)

// 循环次数,从0循环到4
4.times{
    println("测试: ${it}")
}

⑥三目运算符、判断


// 和Java一致,判空还可以简写成这样:
def name = 'd'
def result = name?: "abc"

// 还有用?判空,跟Kotlin的一样,person不为空 → Data属性不为空 → 才打印name
println person?.Data?.name

// asType是类型转换
def s3 = s1.asType(Integer)

⑦闭包


闭包的本质就是代码块,运行时可作为变量传递的函数,并保留定义它们的变量的范围的访问。


// 定义闭包
def clouser = { String param1, int param2 -> // 箭头前面是参数定义,后面是代码
    println "代码部分"
}

// 调用闭包
clouser.call()
clouser()

// 闭包没定义参数的话,隐含一个it参数,和this作用类似
def noParamClosure = { it-> true }

// 函数最后一个参数都是一个闭包,可以省略圆括号,类似于回调函数的用法
task CoderPig {
    doLast ({
      println "Test"  
    })
}

task CoderPig {
    doLast {
      println "Test"  
    }
}

// 闭包里的关键变量,没有闭包嵌套时都指向同一个,有闭包时:
// this:闭包定义处的类;
// owner,delegate:离他最近的哪个闭包对象;

// 闭包委托:每个闭包都有一个delegate对象,Groovy使用该对象来查找
// 不是闭包的局部变量或参数的变量和方法引用,就是代理模式
class Info {
    int id;
    String code;

    def log() {
        println("code:${code};id:${id}")
    }
}

def info(Closure<Info> closure) {
    Info p = new Info()
    closure.delegate = p
    // 委托模式优先
    closure.setResolveStrategy(Closure.DELEGATE_FIRST)
    closure(p)
}

task configClosure {
    doLast {
        info {
            code = "cix"
            id = 1
            log()
        }
    }
}

// 输出:Task :configClosure
// code:cix;id:1

⑧集合


// 数组,定义方式扩展如下,其他和Java类似
def array1 = [12345] as int[]
int[] array2 = [12345]

/* List:链表,对应ArrayList,变量由[]定义,元素可以是任何对象。*/

// 定义列表
def testList = [123

// 添加元素,左移位添加新元素
testList << 300;    
testList.add(6)
testList.leftShift(7)

// 删除
testList.remove(7)
testList.removeAt(7)
testList.removeElement(6)
testList.removeAll { return it % 2 == 0 }   // 自定义规则

// 查找
int result = testList.find { return it % 2 == 0 }
def result2 = testList.findAll { return it % 2 != 0 }
def result3 = testList.any { return it % 2 != 0 }
def result4 = testList.every { return it % 2 == 0 }

// 获取最小值、最大值、满足条件的数量
list.min()
list.max(return Math.abs(it))
def num = findList.count { return it >= 2 }

//排序
testList.sort()      
sortList.sort { a, b -> 
    a == b ?0 : 
            Math.abs(a) < Math.abs(b) ? 1 : -1


/* Map:键值表,对应LinkedHashMap,使用 : 冒号定义,key必须为字符串,可以不用引号包裹 */

// 存取
aMap.keyName
aMap['keyName']
aMap.anotherkey = "i am map"
aMap.anotherkey = [a: 1, b: 2]

// 遍历
def result = ""
[a:1, b:2].each { key, value -> 
    result += "$key$value" 
}
// 带索引遍历(从0开始的计数器,两个参数时传递的Map.Entry对象)
[a:1, b:2].eachWithIndex { entry,index, -> 
    result += "$entry$index" 
}

[a:1, b:2].eachWithIndex  { key, value,index, -> 
    result += "$key$value$index" 
}

// 分组
def group = students.groupBy { def student ->
    return student.value.score >= 60 ? '及格' : '不及格'
}

/* Range,范围,对List的一种扩展 */
def range = 1..5
println(range)  //输出:[1, 2, 3, 4, 5]
range.size()  // 长度
range.iterator() // 迭代器
def s1 = range.get(1)   // 获取标号为1的元素
range.contains(5)  // 是否包含元素5
range.last()    // 最后一个元素
range.remove(1// 移除标号为1的元素
range.clear()   // 清空列表
println("第一个数据: "+range.from) //第一个数据
println("最后一个数据: "+range.to)   //最后一个数据

4. Project(项目)


Gradle的构建由一个或多个Project组成,一个打印Project信息的示例如下(build.gradle),创建了两个子module:


// 配置信息
description("一个测试项目")
version("v0.0.1")
group("Test")

// 定义一个打印项目的方法
def printProject(pj) {
    println("===============")
    println("获取项目信息:")
    println("项目名:" + pj.name)
    println("路径:" + pj.path)
    println("项目说明:" + pj.description)
    println("包含构建脚本的目录:" + pj.projectDir)
    println("生成文件目录:" + pj.buildDir)
    println("属于哪个组:" + pj.group)
    println("版本:" + pj.version)
}

// 获取所有项目信息
def getAllProjectInfo() {
    // 根项目
    printProject(project)
    // 所有子项目,调用getAllprojects()可获得所有项目,即包含根项目
    this.getSubprojects().eachWithIndex { Project entry, int i ->
        printProject(entry)
    }
}

// 调用方法
getAllProjectInfo()

运行结果如下:



还可以对某个Project进行配置,如:


project('module1') {
    description("子模块1号")
    version("v0.0.1")
    group("Test")
}

更多API可进入Project类中自行查看:



 5. 任务(Task)


①创建任务


/* === ① 创建一个名为customTask1的Task,使用gradlew customTask1执行此Task === */
def customTask1 = task("customTask1")
customTask1.doFirst {
    println "Task【${name}】 → 执行之前"
}

customTask1.doLast {
    println "Task【${name}】 → 执行之后"
}

// 输出:> Task :customTask1
// Task【customTask1】 → 执行之前
// Task【customTask1】 → 执行之后


/* ========== ② Map方式仅限相关配置,如:创建时指定分组 ====== */
def customTask2 = task(group: 'test'"customTask2")
customTask2.doLast {
    println("任务:" + name + " 属于分组:" + customTask2.group)
}

// 输出:> Task :customTask2
// 任务:customTask2 属于分组:test


/* ================= ③ 闭包方式实现 =================== */
task customTask3 {
    group("test")
    description("任务名+闭包创建任务")
    doLast {
        println("任务:" + name + " 属于分组:" + customTask2.group)
    }
}

// 输出:> Task :customTask3
// 任务:customTask3 属于分组:test


/* ======================= ④ 通过TaskContainer创建 ==================== */
tasks.create("customTask4") {
    group("test")
    description("通过TaskContainer创建")
    doLast {
        println("任务:" + name + " 属于分组:" + customTask2.group)
    }
}

// 输出:> Task :customTask4
// 任务:customTask4 属于分组:test

/* ==================== ⑤ 自定义Task =================== */
class CustomTask extends DefaultTask {
    // TaskAction注解表示Task本身要执行的方法
    @TaskAction
    def doSomeThing() {
        println("执行一些操作")
    }
}

task(type: CustomTask, "customTask5") {
    doFirst { println "任务执行前1" }
    doLast { println "任务执行后" }
    doFirst { println "任务执行前2" }
}
// 输出:> Task :customTask5
// 任务执行前2
// 任务执行前1
// 执行一些操作
// 任务执行后

/* ==================== 附:Map中可使用的配置 =================== */
// type → 基于一个已存在的Task来创建,类似于类的继承,默认值DefaultTask
// overwrite → 是否替换存在的Task,一般和type配合使用,默认值false
// dependsOn → 配置当前任务的依赖,默认值[]
// action → 添加到任务中的一个Action或者是一个闭包,默认值为null
// description → 任务描述,默认值null
// group → 任务分组,默认值null

②访问任务


// 通过属性名访问,创建的任务都会作为Project的一个属性,属性名就是任务名
customTask3.name

// 通过TaskContainer集合方式访问
tasks['customTask1'].name

// 通过TaskContainer的find或者get方式访问
// 前者找不到返回null,后者找不到会抛UnknownTaskException异常
tasks.findByPath(":customTask1")
tasks.findByName("customTask1")
tasks.getByPath(":customTask1")
tasks.getByName("customTask1")

// 通过任务名称访问
customTask1.name

③任务执行


Gradle 任务执行过程中,可通过doFirst和doLast在任务执行前或后进行任务的相关配置。例子可参见上面的⑤自定义Task,另外,还可以通过getActions()获得所有可执行的Action。


④任务排序


可通过shoundRunAftermustRunAfter方法来控制两个任务谁先执行;


⑤任务的启用和禁用


修改Task的enabled属性即可,默认true,表示启用,如:customTask1.enable = true


⑥任务的onlyIf断言


断言是条件表达式,Task对象有一个onlyIf的方法,该方法可以接收一个闭包作为参数,闭包返回true,则该任务执行,这样可以通过任务的断言来控制哪些任务需要执行。如:按需打包渠道包。


⑦任务规则


创建的任务都在TaskContain中,可以通过它的addRule方法添加响应的任务规则。


⑧任务输入输出


Task提供inputsoutputs属性。


⑨挂载自定义的Task到构建过程中


在Task执行过程中,调用另一个task的execute()方法即可。

注:execute()在Gradle 5.0后就移除了,谷歌了一下,发现只能定义一个task,然后dependsOn或者其他操作符执行一波。


Tips:大概对API有个印象即可,后面研究Script、插件原理啥的会深入讲解~

本文为系列篇(1),大家稍等后续推文。

参考文献

掘金小册:《Mastering Gradle》
https://juejin.cn/book/6844733819363262472

构建工具的进化:ant, maven, gradle
https://zhuanlan.zhihu.com/p/24429133

维基百科:Gradle
https://zh.wikipedia.org/wiki/Gradle

深度探索 Gradle 自动化构建技术(二、Groovy 筑基篇)
https://juejin.cn/post/6844904128594853902

Gradle 爬坑指南 -- 导论
https://juejin.cn/post/6882178101191639053

这一次,彻底了解 Gradle 吧!
https://mp.weixin.qq.com/s/UlnHuM2-Dqad3MXE77xJyg

https://juejin.cn/post/6932813521344430094




最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读

新技术又又又又又又叒来了?
LeakCanary 新版 2.x ,你应该知道的知识点
玩转Android AOP  ,这3个案例你需要掌握!


点击 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存